package logica;

import java.util.List;

import javax.xml.crypto.Data;

import logica.pessoa.Filial;
import logica.util.Constantes;
import logica.veiculo.Acessorio;
import logica.veiculo.Combustivel;
import logica.veiculo.Cor;
import logica.veiculo.Estado;
import logica.veiculo.Potencia;
import logica.util.Constantes;



public class Veiculo {
	
	// Atributos do veiculo
	
	private String Renavam;
	private String modelo;
	private String marca;
	private int ano;
	private int potencia;
	private Potencia tipoDePotencia;
	private Cor cor;
	private Combustivel tipoDeCombustivel;
	private Data dataDeAquisicao;
	private Filial filial;
	private List<Acessorio> acessorios;
	private Estado estado;
	private String placa;
	private int nivelDoTanque;

	/**
	 * Constroi um objeto do tipo Veiculo
	 * @param Renavam O numero RENAVAM do veiculo
	 * @param modelo O modelo do veiculo
	 * @param marca A marca do veiculo
	 * @param ano O ano de fabricacao do veiculo
	 * @param potencia O valor da potencia do veiculo
	 * @param tipoDePotencia O tipo de potencia do veiculo
	 * @param cor A cor do veiculo
	 * @param tipoDeCombustivel O tipo de combustivel utilizado pelo veiculo
	 * @param dataDeAquisicao A data de aquisicao do veiculo pela locadora
	 * @param filial A filial onde esta o veiculo
	 * @param acessorios Os acessorios que o veiculo possui
	 * @param estado O estado do veiculo para a locadora
	 * @param placa A placa do veiculo
	 * @param nivelDoTanque O nivel do tanque do veiculo
	 * @throws Exception Se o RENAVAM, o modelo, a marca ou a placa forem nulos ou vazios ou se o RENAVAM contiver algum caractere nao-numerico ou se a lista de acessorios for vazia ou se o valor da potencia ou do ano forem menores ou iguais a 0 ou se a data, ou o tipo de potencia, ou a cor, ou a filial, ou a lista de acessorios ou o estado for um objeto nulo.
	 */
	
	// Mudar tipo de Acessorio de List para Set
	
	public Veiculo(String Renavam, String modelo, String marca, int ano,
			int potencia, Potencia tipoDePotencia, Cor cor,
			Combustivel tipoDeCombustivel, Data dataDeAquisicao, Filial filial,
			List<Acessorio> acessorios, Estado estado, String placa, int nivelDoTanque) throws Exception {

			verificaEntradas(Renavam, modelo, marca, ano, potencia, tipoDePotencia, cor, tipoDeCombustivel, dataDeAquisicao, filial, acessorios, estado, placa, nivelDoTanque);
		
			this.Renavam = Renavam;
			this.modelo = modelo;
			this.marca = marca;
			this.ano = ano;
			this.potencia = potencia;
			this.tipoDePotencia = tipoDePotencia;
			this.cor = cor;
			this.tipoDeCombustivel = tipoDeCombustivel;
			this.dataDeAquisicao = dataDeAquisicao;
			this.filial = filial;
			this.acessorios = acessorios;
			this.estado = estado;
			this.placa = placa;

	}

	private void verificaEntradas(String Renavam, String modelo, String marca, int ano,
			int potencia, Potencia tipoDePotencia, Cor cor,
			Combustivel tipoDeCombustivel, Data dataDeAquisicao, Filial filial, List<Acessorio> acessorios,
			Estado estado, String placa, int nivelDoTanque)
			throws Exception {
		if ( Renavam == null || Renavam.trim().isEmpty() 
				|| !(validaRenavam(Renavam))) {
			throw new Exception("RENAVAM invalido.");
		} else if (modelo == null || modelo.trim().isEmpty()) {
			throw new Exception("Modelo invalido.");
		} else if (marca == null || marca.trim().isEmpty()) {
			throw new Exception("Marca invalida.");
		} else if (ano <= 0) {
			throw new Exception("Ano de fabricacao invalido.");
		} else if (potencia <= 0) {
			throw new Exception("Potencia invalida.");
		} else if (tipoDePotencia == null) {
			throw new Exception("Tipo de potencia invalido.");
		} else if (cor == null) {
			throw new Exception("Cor invalida.");
		} else if (tipoDeCombustivel == null) {
			throw new Exception("Tipo de combustivel invalido.");
		} else if (dataDeAquisicao == null) {
			throw new Exception("Data invalida.");
		} else if (filial == null) {
			throw new Exception("Filial invalida.");
		} else if (acessorios == null || acessorios.isEmpty()) {
			throw new Exception("Lista de acessorios invalida.");
		} else if (estado == null) {
			throw new Exception("Estado invalido.");
		} else if (placa == null || placa.trim().isEmpty() || ! validaPlaca(placa)) {
			throw new Exception("Placa invalida.");
		} else if (nivelDoTanque < 0) {
			throw new Exception("Nivel do tanque invalido.");
		}
	}

	/**
	 * Mostra o modelo do carro
	 * @return O modelo do carro
	 */
	
	public String getModelo() {
		return modelo;
	}

	/**
	 * Muda o modelo do carro
	 * @param modelo O novo modelo do carro
	 * @throws Exception Se o novo modelo for nulo ou vazio
	 */
	
	public void setModelo(String modelo) throws Exception {
		if (modelo.trim().isEmpty() || modelo == null) {
			throw new Exception("Modelo invalido.");
		}
		this.modelo = modelo;
	}

	/**
	 * Mostra a marca do carro
	 * @return A marca do carro
	 */
	
	public String getMarca() {
		return marca;
	}

	/**
	 * Muda a marca do carro
	 * @param marca A nova marca do carro
	 */
	
	public void setMarca(String marca) throws Exception {
		if (marca == null || marca.trim().isEmpty()) {
			throw new Exception("Marca invalida.");
		} 
		this.marca = marca;
	}

	/**
	 * Mostra a cor do carro
	 * @return a cor do carro
	 */
	
	public Cor getCor() {
		return cor;
	}

	/**
	 * Muda a cor do carro
	 * @param cor A nova cor do carro
	 */
	
	public void setCor(Cor cor) throws Exception {
		if (cor == null) {
			throw new Exception("Cor invalida.");
		} 
		this.cor = cor;
	}

	/**
	 * Mostra o tipo de combustivel do carro
	 * @return O tipo de combustivel do carro
	 */
	
	public Combustivel getTipoDeCombustivel() {
		return tipoDeCombustivel;
	}

	/**
	 * Muda o tipo de combustivel do carro
	 * @param tipoDeCombustivel O novo tipo de combustivel do carro
	 */
	
	public void setTipoDeCombustivel(Combustivel tipoDeCombustivel) throws Exception {
		if (tipoDeCombustivel == null) {
			throw new Exception("Tipo de combustivel invalido.");
		} 
		this.tipoDeCombustivel = tipoDeCombustivel;
	}

	/**
	 * Mostra a data de aquisicao do carro
	 * @return A data de aquisicao do carro
	 */
	
	public Data getDataDeAquisicao() {
		return dataDeAquisicao;
	}

	/**
	 * Muda a data de aquisicao do veiculo
	 * @param dataDeAquisicao A nova data de aquisicao do veiculo
	 */
	
	public void setDataDeAquisicao(Data dataDeAquisicao) {
		this.dataDeAquisicao = dataDeAquisicao;
	}

	/**
	 * Mostra a filial onde esta o carro
	 * @return A filial onde esta o carro
	 */
	
	public Filial getFilial() {
		return filial;
	}

	/**
	 * Muda a filial onde esta o carro
	 * @param filial A nova filial do carro
	 */
	
	public void setFilial(Filial filial) throws Exception {
		if (filial == null) {
			throw new Exception("Filial invalida.");
		} 
		this.filial = filial;
	}

	/**
	 * Mostra a lista de acessorios do carro
	 * @return A lista de acessorios do carro
	 */
	
	public List<Acessorio> getAcessorios() {
		return acessorios;
	}

	/**
	 * Muda a lista de acessorios do carro
	 * @param acessorios A nova lista de acessorios do carro
	 * @throws Exception Se a lista de acessorios for vazia
	 */
	
	public void setAcessorios(List<Acessorio> acessorios) throws Exception {
		if (acessorios == null || acessorios.isEmpty()) {
			throw new Exception("Lista de acessorios vazia.");
		}
		this.acessorios = acessorios;
	}

	/**
	 * Mostra o estado do carro
	 * @return O estado do carro
	 */
	
	public Estado getEstado() {
		return estado;
	}

	/**
	 * Muda o estado do carro
	 * @param estado O novo estado do carro
	 */
	
	public void setEstado(Estado estado) throws Exception {
		if (estado == null) {
			throw new Exception("Estado invalido.");
		}
		this.estado = estado;
	}
	
	/**
	 * Mostra o numero do RENAVAM do carro
	 * @return o numero do RENAVAM do carro
	 */
	
	public String getRenavam() {
		return Renavam;
	}

	/**
	 * Muda o numero do RENAVAM do carro
	 * @param RENAVAM O novo numero do RENAVAM do carro
	 * @throws Exception Se o novo RENAVAM for nulo, vazio ou contiver caracteres nao-numericos
	 */
	
	public void setRenavam(String Renavam) throws Exception {
		if (Renavam.trim().isEmpty() || Renavam == null 
			|| !(validaRenavam(Renavam))) {
			throw new Exception("RENAVAM invalido.");
		}
		this.Renavam = Renavam;
	}
	
	/**
	 * Mostra o ano de fabricacao do carro
	 * @return o ano de fabricacao do carro
	 */
	
	public int getAno() {
		return ano;
	}

	/**
	 * Muda o ano de fabricacao do carro
	 * @param ano O novo ano de fabricacao do carro
	 */
	
	public void setAno(int ano) {
		this.ano = ano;
	}

	/**
	 * Mostra a potencia do carro
	 * @return a potencia do carro
	 */
	
	public int getPotencia() {
		return potencia;
	}	
	
	/**
	 * Muda o valor da potencia do carro
	 * @param potencia O valor da potencia do carro
	 * @throws Exception Se a potencia for menor ou igual a 0
	 */
	
	public void setPotencia(int potencia) throws Exception {
		if (potencia <= 0) {
			throw new Exception("Potencia invalida.");
		}
		this.potencia = potencia;
	}
	
	/**
	 * Mostra o tipo de potencia do carro
	 * @return o tipo de potencia do carro
	 */
	
	public Potencia getTipoDePotencia() {
		return tipoDePotencia;
	}

	public void setTipoDePotencia(Potencia tipoDePotencia) throws Exception {
		if (tipoDePotencia == null) {
			throw new Exception("Tipo de potencia invalido.");
		} 
		this.tipoDePotencia = tipoDePotencia;
	}
	
	/**
	 * Mostra a placa do carro
	 * @return a placa do carro
	 */
	
	public String getPlaca() {
		return placa;
	}
	
	/**
	 * Mudar a placa do carro
	 * @param placa A nova placa do carro
	 * @throws Exception Se a placa do carro for nula ou vazia ou tiver um formato nao-padrao
	 */
	
	public void setPlaca(String placa) throws Exception {
		if (placa.trim().isEmpty() || placa == null || ! validaPlaca(placa)) {
			throw new Exception("Placa invalida.");
		}
		this.placa = placa;
	}
	
	/**
	 * Mostra o nivel do tanque do carro
	 * @return O nivel do tanque do carro
	 */
	
	public int getNivelDoTanque() {
		return nivelDoTanque;
	}	
	
	/**
	 * Muda o valor do nivel do tanque do carro
	 * @param nivelDoTanque O valor do nivel do tanque do carro
	 * @throws Exception Se o nivel do tanque for menor que 0
	 */
	
	public void setNivelDoTanque(int nivelDoTanque) throws Exception {
		if (nivelDoTanque < 0) {
			throw new Exception("Nivel do tanque invalido.");
		}
		this.nivelDoTanque = nivelDoTanque;
	}
	
	// Verifica a validade do numero RENAVAM informado
	
	private boolean validaRenavam(String RENAVAM) {
		char[] arrayDeDigitos = RENAVAM.toCharArray();

		for (int i = 0; i < arrayDeDigitos.length; i++) {
			if (!Character.isDigit(arrayDeDigitos[i])) {
				return false;
			}
		}

		return true;
	}
	
	// Verifica a validade da placa informada
	
	private boolean validaPlaca(String placa) {
		char[] arrayPlaca = placa.toCharArray();
		
		for(int i = 0; i < Constantes.NUMERO_DE_LETRAS_DA_PLACA; i++) {
			if (!Character.isLetter(arrayPlaca[i])) {
				return false;
			}
		}
		
		for(int i = Constantes.NUMERO_DE_LETRAS_DA_PLACA; i < arrayPlaca.length; i++) {
			if (!Character.isDigit(arrayPlaca[i])) {
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * Adiciona um novo acessorio a lista de acessorios.
	 * @param novoAcessorio Acessorio - O novo acessorio a ser adicionado na lista
	 */
	
	public void adicionaAcessorio(Acessorio novoAcessorio) {
		if (!getAcessorios().contains(novoAcessorio)) {
			getAcessorios().add(novoAcessorio);
		}
		
	}
	
	/**
	 * Remove um acessorio da lista de acessorios.
	 * @param acessorioASerRemovido Acessorio - O acessorio a ser removido da lista
	 */
	
	public void removeAcessorio(Acessorio acessorioASerRemovido) {
		if (getAcessorios().contains(acessorioASerRemovido)) {
			getAcessorios().remove(acessorioASerRemovido);
		}
		
	}
	
	/**
	 * Compara um objeto Veiculo com um objeto qualquer.
	 * @return boolean - <i>True</i> se os objetos sao iguais, <i>false</i> caso contrario
	 */
	
	public boolean equals(Object objeto) {
		if (! (objeto instanceof Veiculo)) {
			return false;
		}
		
		Veiculo outroVeiculo = (Veiculo) objeto;
		return this.getRenavam().equals(outroVeiculo.getRenavam()) && this.getPlaca().equals(outroVeiculo.getPlaca());
	}
	
	

}
